﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Rhino.Mocks;
using Chefbook.DataAccess;
using Chefbook.DataObjects;
using Chefbook.ViewModels;
using System.Collections.Specialized;

namespace UnitTests.ViewModelsTests
{
    [TestFixture]
    public class RecipeViewModelFixture 
    {
        private RecipeViewModel recipeVM;
        private Recipe recipe;

        [SetUp]
        public void Init()
        {
            recipe = new Recipe(null);
            recipe.CookTime = TimeSpan.FromMinutes(90);
            recipe.PrepTime = TimeSpan.FromMinutes(90);
            recipeVM = new RecipeViewModel(recipe);
        }

        [Test]
        public void IngredientsAssignedBeforeSettingModelShouldNotBeUndoable()
        {
            var ing1 = new Ingredient(recipe);
            recipeVM.AddIngredient(new IngredientViewModel(new Ingredient()));
            recipeVM = new RecipeViewModel();
            recipeVM.RecipeModel = recipe;
            recipeVM.AddIngredient(new IngredientViewModel(new Ingredient()));
            Assert.AreEqual(3, recipeVM.Ingredients.Count);
            Assert.True(recipeVM.Undo());
            Assert.AreEqual(2, recipeVM.Ingredients.Count);
            Assert.False(recipeVM.Undo());
            Assert.False(recipeVM.Undo());
            Assert.AreEqual(2, recipeVM.Ingredients.Count);
        }

        [Test]
        public void ChangingNameFiresPropertyChangedTest()
        {
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Name = "Hello", "Name");
            Assert.AreEqual("Hello", recipe.Name);
            Assert.AreEqual("Hello", recipeVM.Name);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Name = "Hello");
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Name = "World", "Name");
            Assert.AreEqual("World", recipe.Name);
            Assert.AreEqual("World", recipeVM.Name);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "Name");
            Assert.AreEqual("Hello", recipe.Name);
            Assert.AreEqual("Hello", recipeVM.Name);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "Name");
            Assert.AreEqual(string.Empty, recipeVM.Name);
            Assert.AreEqual(string.Empty, recipe.Name);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Redo(), "Name");
            Assert.AreEqual("Hello", recipe.Name);
            Assert.AreEqual("Hello", recipeVM.Name);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Redo(), "Name");
            Assert.AreEqual("World", recipe.Name);
            Assert.AreEqual("World", recipeVM.Name);
        }

        [Test]
        public void ChangingDescriptionFiresPropertyChangedTest()
        {
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Description = "Hello", "Description");
            Assert.AreEqual("Hello", recipe.Description);
            Assert.AreEqual("Hello", recipeVM.Description);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Description = "Hello");
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Description = "World", "Description");
            Assert.AreEqual("World", recipe.Description);
            Assert.AreEqual("World", recipeVM.Description);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "Description");
            Assert.AreEqual("Hello", recipe.Description);
            Assert.AreEqual("Hello", recipeVM.Description);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "Description");
            Assert.AreEqual(string.Empty, recipeVM.Description);
            Assert.AreEqual(string.Empty, recipe.Description);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Redo(), "Description");
            Assert.AreEqual("Hello", recipe.Description);
            Assert.AreEqual("Hello", recipeVM.Description);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Redo(), "Description");
            Assert.AreEqual("World", recipe.Description);
            Assert.AreEqual("World", recipeVM.Description);
        }

        [Test]
        public void ChangingDirectionsFiresPropertyChangedTest()
        {
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Directions = "Hello", "Directions");
            Assert.AreEqual("Hello", recipe.Directions);
            Assert.AreEqual("Hello", recipeVM.Directions);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Directions = "Hello");
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Directions = "World", "Directions");
            Assert.AreEqual("World", recipe.Directions);
            Assert.AreEqual("World", recipeVM.Directions);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "Directions");
            Assert.AreEqual("Hello", recipe.Directions);
            Assert.AreEqual("Hello", recipeVM.Directions);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "Directions");
            Assert.AreEqual(string.Empty, recipeVM.Directions);
            Assert.AreEqual(string.Empty, recipe.Directions);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Redo(), "Directions");
            Assert.AreEqual("Hello", recipe.Directions);
            Assert.AreEqual("Hello", recipeVM.Directions);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Redo(), "Directions");
            Assert.AreEqual("World", recipe.Directions);
            Assert.AreEqual("World", recipeVM.Directions);
        }

        [Test]
        public void ChangingCookMinutesFiresPropertyChangedTest()
        {
            recipeVM.AssertPropertyChangedFired(() => recipeVM.CookTimeMinutes = 15, "CookTimeMinutes");
            Assert.AreEqual(15, recipeVM.CookTimeMinutes);
            Assert.AreEqual(1, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(75), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.CookTimeMinutes = 15); //No change
            Assert.AreEqual(15, recipeVM.CookTimeMinutes);
            Assert.AreEqual(1, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(75), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.CookTimeMinutes = 45, "CookTimeMinutes");
            Assert.AreEqual(45, recipeVM.CookTimeMinutes);
            Assert.AreEqual(1, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(105), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "CookTimeMinutes");
            Assert.AreEqual(15, recipeVM.CookTimeMinutes);
            Assert.AreEqual(1, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(75), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "CookTimeMinutes");
            Assert.AreEqual(30, recipeVM.CookTimeMinutes);
            Assert.AreEqual(1, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(90), recipe.CookTime);
        }

        [Test]
        public void ChangingCookMinutesFiresPropertyChangedTest2()
        {
            recipeVM.AssertPropertyChangedFired(() => recipeVM.CookTimeMinutes = 75, "CookTimeMinutes", "CookTimeHours");
            Assert.AreEqual(15, recipeVM.CookTimeMinutes);
            Assert.AreEqual(2, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(135), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.CookTimeMinutes = 15); //No change
            Assert.AreEqual(15, recipeVM.CookTimeMinutes);
            Assert.AreEqual(2, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(135), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.CookTimeMinutes = 45, "CookTimeMinutes");
            Assert.AreEqual(45, recipeVM.CookTimeMinutes);
            Assert.AreEqual(2, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(165), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "CookTimeMinutes");
            Assert.AreEqual(15, recipeVM.CookTimeMinutes);
            Assert.AreEqual(2, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(135), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "CookTimeMinutes", "CookTimeHours");
            Assert.AreEqual(30, recipeVM.CookTimeMinutes);
            Assert.AreEqual(1, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(90), recipe.CookTime);
        }

        [Test]
        public void ChangingCookTimeHoursFiresPropertyChangedTest()
        {
            recipeVM.AssertPropertyChangedFired(() => recipeVM.CookTimeHours = 2, "CookTimeHours");
            Assert.AreEqual(30, recipeVM.CookTimeMinutes);
            Assert.AreEqual(2, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(150), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.CookTimeHours = 2); //No change
            Assert.AreEqual(30, recipeVM.CookTimeMinutes);
            Assert.AreEqual(2, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(150), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.CookTimeHours = 3, "CookTimeHours");
            Assert.AreEqual(30, recipeVM.CookTimeMinutes);
            Assert.AreEqual(3, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(210), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "CookTimeHours");
            Assert.AreEqual(30, recipeVM.CookTimeMinutes);
            Assert.AreEqual(2, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(150), recipe.CookTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "CookTimeHours");
            Assert.AreEqual(30, recipeVM.CookTimeMinutes);
            Assert.AreEqual(1, recipeVM.CookTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(90), recipe.CookTime);
        }

        [Test]
        public void ChangingPrepMinutesFiresPropertyChangedTest()
        {
            recipeVM.AssertPropertyChangedFired(() => recipeVM.PrepTimeMinutes = 15, "PrepTimeMinutes");
            Assert.AreEqual(15, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(1, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(75), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.PrepTimeMinutes = 15); //No Change
            Assert.AreEqual(15, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(1, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(75), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.PrepTimeMinutes = 45, "PrepTimeMinutes");
            Assert.AreEqual(45, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(1, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(105), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "PrepTimeMinutes");
            Assert.AreEqual(15, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(1, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(75), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "PrepTimeMinutes");
            Assert.AreEqual(30, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(1, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(90), recipe.PrepTime);
        }

        [Test]
        public void ChangingPrepMinutesFiresPropertyChangedTest2()
        {
            recipeVM.AssertPropertyChangedFired(() => recipeVM.PrepTimeMinutes = 75, "PrepTimeMinutes", "PrepTimeHours");
            Assert.AreEqual(15, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(2, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(135), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.PrepTimeMinutes = 75, "PrepTimeHours");
            Assert.AreEqual(15, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(3, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(195), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "PrepTimeHours");
            Assert.AreEqual(15, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(2, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(135), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "PrepTimeMinutes", "PrepTimeHours");
            Assert.AreEqual(30, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(1, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(90), recipe.PrepTime);
        }

        [Test]
        public void ChangingPrepTimeHoursFiresPropertyChangedTest()
        {
            recipeVM.AssertPropertyChangedFired(() => recipeVM.PrepTimeHours = 2, "PrepTimeHours");
            Assert.AreEqual(30, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(2, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(150), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.PrepTimeHours = 2); //No change
            Assert.AreEqual(30, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(2, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(150), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.PrepTimeHours = 1, "PrepTimeHours");
            Assert.AreEqual(30, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(1, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(90), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "PrepTimeHours");
            Assert.AreEqual(30, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(2, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(150), recipe.PrepTime);
            recipeVM.AssertPropertyChangedFired(() => recipeVM.Undo(), "PrepTimeHours");
            Assert.AreEqual(30, recipeVM.PrepTimeMinutes);
            Assert.AreEqual(1, recipeVM.PrepTimeHours);
            Assert.AreEqual(TimeSpan.FromMinutes(90), recipe.PrepTime);
        }

        [Test]
        public void AddIngredientFiresCollectionChangedTest()
        {
            var ing = new Ingredient();
            IngredientViewModel ingredient1 = new IngredientViewModel(ing);
            IngredientViewModel ingredient2 = new IngredientViewModel(new Ingredient());
            PerformCollectionActionAndAssertCollectionChangedEventFired(recipeVM.Ingredients, ingredient1, NotifyCollectionChangedAction.Add);
            PerformCollectionActionAndAssertCollectionChangedEventFired(recipeVM.Ingredients, ingredient2, NotifyCollectionChangedAction.Add);
            var args = recipeVM.Ingredients.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Undo());
            Assert.AreEqual(NotifyCollectionChangedAction.Remove, args.Action);
            Assert.Contains(ingredient2, args.OldItems);
            args = recipeVM.Ingredients.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Undo());
            Assert.AreEqual(NotifyCollectionChangedAction.Remove, args.Action);
            Assert.Contains(ingredient1, args.OldItems);
            args = recipeVM.Ingredients.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Redo());
            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.Contains(ingredient1, args.NewItems);
            args = recipeVM.Ingredients.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Redo());
            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.Contains(ingredient2, args.NewItems);
        }

        [Test]
        public void RemoveIngredientFiresCollectionChangedTest()
        {
            IngredientViewModel ingredient1 = new IngredientViewModel(new Ingredient());
            IngredientViewModel ingredient2 = new IngredientViewModel(new Ingredient());
            recipeVM.AddIngredient(ingredient1);
            recipeVM.AddIngredient(ingredient2);
            PerformCollectionActionAndAssertCollectionChangedEventFired(recipeVM.Ingredients, ingredient1, NotifyCollectionChangedAction.Remove);
            PerformCollectionActionAndAssertCollectionChangedEventFired(recipeVM.Ingredients, ingredient2, NotifyCollectionChangedAction.Remove);
            var args = recipeVM.Ingredients.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Undo());
            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.Contains(ingredient2, args.NewItems);
            args = recipeVM.Ingredients.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Undo());
            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.Contains(ingredient1, args.NewItems);
            args = recipeVM.Ingredients.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Redo());
            Assert.AreEqual(NotifyCollectionChangedAction.Remove, args.Action);
            Assert.Contains(ingredient1, args.OldItems);
            args = recipeVM.Ingredients.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Redo());
            Assert.AreEqual(NotifyCollectionChangedAction.Remove, args.Action);
            Assert.Contains(ingredient2, args.OldItems);
        }

        [Test]
        public void DeleteOnIngredientRemovesItFromList()
        {
            IngredientViewModel ingredient = new IngredientViewModel(new Ingredient());
            recipeVM.AddIngredient(ingredient);
            Assert.AreEqual(1, recipeVM.Ingredients.Count);
            Assert.Contains(ingredient, recipeVM.Ingredients);
            ingredient.RemoveIngredientCommand.Execute(null);
            Assert.AreEqual(0, recipeVM.Ingredients.Count);
            Assert.False(recipeVM.Ingredients.Contains(ingredient));
            recipeVM.Undo();
            Assert.AreEqual(1, recipeVM.Ingredients.Count);
            Assert.Contains(ingredient, recipeVM.Ingredients);
            recipeVM.Redo();
            Assert.AreEqual(0, recipeVM.Ingredients.Count);
            Assert.False(recipeVM.Ingredients.Contains(ingredient));
        }

        [Test]
        public void AddCategoryFiresCollectionChangedTest()
        {
            CategoryViewModel categoryVM1 = new CategoryViewModel(new Category(null));
            CategoryViewModel categoryVM2 = new CategoryViewModel(new Category(null));
            PerformCollectionActionAndAssertCollectionChangedEventFired(recipeVM.Categories, categoryVM1, NotifyCollectionChangedAction.Add);
            PerformCollectionActionAndAssertCollectionChangedEventFired(recipeVM.Categories, categoryVM2, NotifyCollectionChangedAction.Add);
            var args = recipeVM.Categories.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Undo());
            Assert.AreEqual(NotifyCollectionChangedAction.Remove, args.Action);
            Assert.Contains(categoryVM2, args.OldItems);
            args = recipeVM.Categories.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Undo());
            Assert.AreEqual(NotifyCollectionChangedAction.Remove, args.Action);
            Assert.Contains(categoryVM1, args.OldItems);
            args = recipeVM.Categories.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Redo());
            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.Contains(categoryVM1, args.NewItems);
            args = recipeVM.Categories.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Redo());
            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.Contains(categoryVM2, args.NewItems);
        }

        [Test]
        public void RemoveCategoryFiresCollectionChangedTest()
        {
            CategoryViewModel categoryVM1 = new CategoryViewModel(new Category(null) { Name = "Cat1" });
            CategoryViewModel categoryVM2 = new CategoryViewModel(new Category(null) { Name = "Cat2" });
            recipeVM.Name = "TestRecipe";
            recipeVM.AddToCategory(categoryVM1);
            recipeVM.AddToCategory(categoryVM2);
            PerformCollectionActionAndAssertCollectionChangedEventFired(recipeVM.Categories, categoryVM1, NotifyCollectionChangedAction.Remove);
            PerformCollectionActionAndAssertCollectionChangedEventFired(recipeVM.Categories, categoryVM2, NotifyCollectionChangedAction.Remove);
            var args = recipeVM.Categories.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Undo());
            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.Contains(categoryVM2, args.NewItems);
            args = recipeVM.Categories.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Undo());
            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.Contains(categoryVM1, args.NewItems);
            args = recipeVM.Categories.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Redo());
            Assert.AreEqual(NotifyCollectionChangedAction.Remove, args.Action);
            Assert.Contains(categoryVM1, args.OldItems);
            args = recipeVM.Categories.AssertCollectionChangedFiredAndReturnArgs(() => recipeVM.Redo());
            Assert.AreEqual(NotifyCollectionChangedAction.Remove, args.Action);
            Assert.Contains(categoryVM2, args.OldItems);
        }

        [Test]
        public void CategoryPathsReturnsProperValueTest()
        {
            var catBob = new Category() { Name = "Bob" };
            var catTom = new Category(catBob) { Name = "Tom" };
            var catJill = new Category(catTom) { Name = "Jill" };
            var catJane = new Category(catTom) { Name = "Jane" };
            recipe.AddCategory(catJane);
            recipe.AddCategory(catJill);
            Assert.Contains("> Bob > Tom > Jill", recipeVM.CategoryPaths);
            Assert.Contains("> Bob > Tom > Jane", recipeVM.CategoryPaths);
        }

        private void PerformCollectionActionAndAssertCollectionChangedEventFired<T>(INotifyCollectionChanged collection, T viewModelEntry, NotifyCollectionChangedAction action)
        {
            NotifyCollectionChangedEventArgs args = null;
            Action act = null;
            var category = viewModelEntry as CategoryViewModel;
            var ingredient = viewModelEntry as IngredientViewModel;
            if (category != null)
            {
                switch (action)
                {
                    case NotifyCollectionChangedAction.Add:
                        act = () => recipeVM.AddToCategory(category);
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        act = () => recipeVM.RemoveFromCategory(category);
                        break;
                    default:
                        throw new NotImplementedException();
                }
            }
            if (ingredient != null)
            {
                switch (action)
                {
                    case NotifyCollectionChangedAction.Add:
                        act = () => recipeVM.AddIngredient(ingredient);
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        act = () => recipeVM.RemoveIngredient(ingredient);
                        break;
                    default:
                        throw new NotImplementedException();
                }
            }

            args = collection.AssertCollectionChangedFiredAndReturnArgs(act);
            Assert.AreEqual(action, args.Action);
            switch (action)
            {
                case NotifyCollectionChangedAction.Add:
                    Assert.Contains(viewModelEntry, args.NewItems);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    Assert.Contains(viewModelEntry, args.OldItems);
                    break;
                default:
                    throw new NotImplementedException();
            }
            
        }

    }
}